/**
 * 简化版统一请求层：封装HTTP请求，提供拦截器、超时、重试、取消请求等核心能力
 * @class RequestManager
 * @example
 * // 基础使用
 * import requestManager from './RequestManager';
 * requestManager.get('/api/data').then(res => console.log(res.data));
 */
import { showNotification } from "@/utils/showNotification";
class RequestManager {
  /**
   * 构造函数：初始化请求默认配置
   * @constructor
   * @property {string} baseURL - 默认基础请求地址，默认值：http://localhost:3000
   * @property {number} timeout - 默认请求超时时间（毫秒），默认值：30000
   * @property {number} retryCount - 默认请求重试次数，默认值：3
   * @property {number} retryDelay - 默认初始重试延迟（毫秒），默认值：1000
   * @property {Object} interceptors - 拦截器存储容器
   * @property {Function[]} interceptors.request - 请求拦截器数组
   * @property {Function[]} interceptors.response - 响应拦截器数组
   * @property {Map<string, AbortController>} activeRequests - 活跃请求控制器映射（key:请求ID，value:AbortController）
   */
  constructor() {
    this.baseURL = 'http://localhost:3000';
    this.timeout = 30000;
    this.retryCount = 3;
    this.retryDelay = 1000;
    this.interceptors = { request: [], response: [] };
    this.activeRequests = new Map();
  }

  /**
   * 私有方法：统一应用拦截器（请求/响应通用）
   * @private
   * @param {Object} config - 待处理的配置/响应对象
   * @param {string} type - 拦截器类型，可选值：request|response
   * @returns {Promise<Object>} 处理后的配置/响应对象
   */
  async #applyInterceptors(config, type) {
    let processed = { ...config };
    for (const interceptor of this.interceptors[type]) {
      processed = await interceptor(processed);
    }
    return processed;
  }

  /**
   * 私有方法：延迟函数（重试逻辑专用）
   * @private
   * @param {number} ms - 延迟时间（毫秒）
   * @returns {Promise<void>} 延迟完成的Promise
   */
  #delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * 私有方法：核心重试逻辑
   * @private
   * @param {Function} requestFn - 实际发起请求的函数（需返回Promise）
   * @param {number} retries - 剩余重试次数
   * @param {number} delay - 当前重试延迟（毫秒）
   * @returns {Promise<Object>} 请求成功的响应结果
   * @throws {Error} 重试耗尽或主动取消时抛出原错误
   */
  async #retryRequest(requestFn, retries, delay) {
    try {
      return await requestFn();
    } catch (error) {
      // 主动取消（AbortError）不重试，仅网络/超时等错误重试
      if (retries > 0 && error.name !== 'AbortError') {
        console.log(`🔄 重试(${retries}次剩余)，延迟${delay}ms`);
        await this.#delay(delay);
        return this.#retryRequest(requestFn, retries - 1, delay * 2); // 延迟翻倍
      }
      throw error;
    }
  }

  /**
   * 核心请求方法：处理所有HTTP请求的统一入口
   * @param {string} url - 请求地址（相对地址会拼接baseURL，绝对地址直接使用）
   * @param {Object} [options={}] - 请求配置选项
   * @param {string} [options.method='GET'] - 请求方法，可选值：GET|POST|PUT|DELETE
   * @param {Object|FormData} [options.data=null] - 请求体数据（POST/PUT专用）
   * @param {number} [options.retry=this.retryCount] - 当前请求的重试次数（覆盖默认值）
   * @param {number} [options.timeout=this.timeout] - 当前请求的超时时间（覆盖默认值）
   * @param {string} [options.requestId] - 自定义请求ID（默认自动生成）
   * @param {Object} [options.headers={}] - 请求头（会与默认Content-Type合并）
   * @returns {Promise<Object>} 处理后的响应对象
   * @throws {Error} 请求失败（超时、网络错误、主动取消等）时抛出错误
   */
  async request(url, options = {}) {
    // 生成唯一请求ID（默认：req_时间戳_随机字符串）
    const requestId = options.requestId || `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const {
      method = 'GET',
      data = null,
      retry = this.retryCount,
      timeout = this.timeout,
      ...rest
    } = options;

    // 合并请求头：用户headers覆盖默认值，默认Content-Type为application/json
    const defaultHeaders = { 'Content-Type': 'application/json' };
    const config = {
      method,
      headers: { ...defaultHeaders, ...rest.headers },
      signal: new AbortController().signal, // 创建取消信号
      timeout,
      retry
    };
    // 存储当前请求的控制器（用于后续取消）
    this.activeRequests.set(requestId, config.signal.controller);

    // 处理请求体：FormData无需JSON序列化，且自动移除默认Content-Type
    if (data) {
      config.body = data instanceof FormData ? data : JSON.stringify(data);
      if (data instanceof FormData) delete config.headers['Content-Type'];
    }

    // 处理请求地址（绝对地址跳过baseURL拼接）
    const fullUrl = url.startsWith('http') ? url : `${this.baseURL}${url}`;
    // 应用请求拦截器
    const processedConfig = await this.#applyInterceptors(config, 'request');

    // 定义实际发起请求的函数（用于重试逻辑）
    const requestFn = async () => {
      // 超时Promise（超时后触发reject）
      const timeoutPromise = new Promise((_, reject) => {
        setTimeout(() => reject(new Error(`超时(${timeout}ms)：${fullUrl}`)), timeout);
      });

      // 发起请求：超时和正常请求「竞速」，先触发的决定结果
      const response = await Promise.race([
        fetch(fullUrl, processedConfig),
        timeoutPromise
      ]);

      // 校验HTTP状态码（非2xx视为失败）
      if (!response.ok) throw new Error(`HTTP ${response.status}：${response.statusText}`);

      // 处理响应数据：根据Content-Type自动解析（JSON/blob/text）
      const contentType = response.headers.get('content-type') || '';
      const responseData = contentType.includes('application/json')
        ? await response.json()
        : contentType.includes('audio/')
          ? await response.blob()
          : await response.text();

      // 构造统一响应格式，应用响应拦截器
      return this.#applyInterceptors({
        data: responseData, // 解析后的数据
        status: response.status, // HTTP状态码
        statusText: response.statusText, // 状态描述
        headers: response.headers, // 响应头
        requestId // 请求ID（用于日志/调试）
      }, 'response');
    };

    try {
      // 执行请求（带重试逻辑）
      return await this.#retryRequest(requestFn, retry, this.retryDelay);
    } catch (error) {
      // 区分错误类型，返回更清晰的错误信息
      const errorMsg = error.name === 'AbortError'
        ? `请求已取消：${requestId}`
        : error.message;
      throw new Error(`请求失败：${errorMsg}`);
    } finally {
      // 无论成功/失败，清理当前请求的控制器
      this.activeRequests.delete(requestId);
    }
  }

  /**
   * GET请求快捷方法
   * @param {string} url - 请求地址
   * @param {Object} [options={}] - 请求配置（同request方法的options，method固定为GET）
   * @returns {Promise<Object>} 响应对象
   * @example
   * requestManager.get('/api/user', { headers: { 'Token': 'xxx' } });
   */
  get(url, options = {}) {
    return this.request(url, { ...options, method: 'GET' });
  }

  /**
   * POST请求快捷方法
   * @param {string} url - 请求地址
   * @param {Object|FormData} [data=null] - 请求体数据
   * @param {Object} [options={}] - 请求配置（同request方法的options，method固定为POST）
   * @returns {Promise<Object>} 响应对象
   * @example
   * // 普通JSON数据
   * requestManager.post('/api/login', { username: 'xxx', pwd: 'xxx' });
   * // FormData（文件上传）
   * const formData = new FormData();
   * formData.append('avatar', file);
   * requestManager.post('/api/upload', formData);
   */
  post(url, data = null, options = {}) {
    return this.request(url, { ...options, method: 'POST', data });
  }

  /**
   * PUT请求快捷方法
   * @param {string} url - 请求地址
   * @param {Object} [data=null] - 请求体数据
   * @param {Object} [options={}] - 请求配置（同request方法的options，method固定为PUT）
   * @returns {Promise<Object>} 响应对象
   * @example
   * requestManager.put('/api/user/1', { nickname: 'newName' });
   */
  put(url, data = null, options = {}) {
    return this.request(url, { ...options, method: 'PUT', data });
  }

  /**
   * DELETE请求快捷方法
   * @param {string} url - 请求地址
   * @param {Object} [options={}] - 请求配置（同request方法的options，method固定为DELETE）
   * @returns {Promise<Object>} 响应对象
   * @example
   * requestManager.delete('/api/user/1');
   */
  delete(url, options = {}) {
    return this.request(url, { ...options, method: 'DELETE' });
  }

  /**
   * 文件上传快捷方法（基于POST，默认超时60秒）
   * @param {string} url - 上传接口地址
   * @param {File} file - 待上传的File对象
   * @param {Object} [options={}] - 上传配置（同request方法的options，默认timeout=60000）
   * @returns {Promise<Object>} 上传响应对象
   * @example
   * // 监听文件选择器变化
   * fileInput.addEventListener('change', (e) => {
   *   const file = e.target.files[0];
   *   requestManager.upload('/api/upload/avatar', file);
   * });
   */
  upload(url, file, options = {}) {
    const formData = new FormData();
    formData.append('file', file); // 默认文件字段名为file，可通过options自定义
    return this.post(url, formData, { ...options, timeout: 60000 });
  }

  /**
   * 取消单个活跃请求
   * @param {string} requestId - 要取消的请求ID（request方法options中传入或自动生成的ID）
   * @example
   * // 发起请求时记录requestId
   * const requestId = 'userReq123';
   * requestManager.get('/api/user', { requestId }).catch(err => {
   *   if (err.message.includes('请求已取消')) console.log('请求已手动取消');
   * });
   * // 取消请求
   * requestManager.cancelRequest(requestId);
   */
  cancelRequest(requestId) {
    const controller = this.activeRequests.get(requestId);
    if (controller) {
      controller.abort();
      this.activeRequests.delete(requestId);
    }
  }

  /**
   * 取消所有活跃请求
   * @example
   * // 页面卸载时取消所有请求，避免内存泄漏
   * window.addEventListener('beforeunload', () => {
   *   requestManager.cancelAllRequests();
   * });
   */
  cancelAllRequests() {
    this.activeRequests.forEach(controller => controller.abort());
    this.activeRequests.clear();
  }

  /**
   * 添加请求/响应拦截器
   * @param {string} type - 拦截器类型，必须为request或response
   * @param {Function} interceptor - 拦截器函数（需返回处理后的配置/响应对象）
   * @returns {void}
   * @example
   * // 添加请求拦截器：添加Token
   * requestManager.addInterceptor('request', (config) => {
   *   config.headers['Token'] = localStorage.getItem('token');
   *   return config;
   * });
   * // 添加响应拦截器：统一处理401未授权
   * requestManager.addInterceptor('response', (response) => {
   *   if (response.status === 401) {
   *     window.location.href = '/login';
   *   }
   *   return response;
   * });
   */
  addInterceptor(type, interceptor) {
    if (['request', 'response'].includes(type) && typeof interceptor === 'function') {
      this.interceptors[type].push(interceptor);
    }
  }
}

/**
 * 默认请求实例（已配置基础拦截器）
 * @type {RequestManager}
 * @property {Function} get - GET请求方法
 * @property {Function} post - POST请求方法
 * @property {Function} upload - 文件上传方法
 * @property {Function} cancelAllRequests - 取消所有请求方法
 */
const requestManager = new RequestManager();

// 内置请求拦截器：添加请求时间戳（便于日志追踪）
requestManager.addInterceptor('request', (config) => {
  config.headers['X-Request-Time'] = new Date().toISOString();
  return config;
});

// 内置响应拦截器：打印错误日志（非2xx状态码）
requestManager.addInterceptor('response', (response) => {
  if (response.status >= 400) {
    showNotification(`❌ 请求失败[${response.requestId}]：${response.status} ${response.statusText}`);
  }
  return response;
});

export default requestManager;